package com.huatai.bi.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huatai.bi.config.XxlJobConfig;
import com.huatai.bi.dto.XxlJobDTO;
import com.huatai.bi.entity.XxlJobGroupEntity;
import com.huatai.bi.entity.XxlJobInfoEntity;
import com.huatai.bi.entity.XxlJobLogglueEntity;
import com.huatai.bi.mapper.XxlJobInfoMapper;
import com.huatai.bi.service.XxlJobGroupService;
import com.huatai.bi.service.XxlJobInfoService;
import com.huatai.bi.service.XxlJobLogglueService;
import com.huatai.bi.utils.XxlJobUtil;
import com.huatai.bi.vo.XxlJobDictVo;
import com.huatai.common.error.exception.ServiceException;
import com.xxl.job.core.glue.GlueTypeEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 *
 */
@Service
public class XxlJobInfoServiceImpl extends ServiceImpl<XxlJobInfoMapper, XxlJobInfoEntity>
    implements XxlJobInfoService{

	@Autowired
	private XxlJobConfig xxlJobConfig;

	@Autowired
	private XxlJobUtil xxlJobUtil;

	@Autowired
	private XxlJobLogglueService xxlJobLogglueService;
	@Autowired
	XxlJobGroupService groupService;
	@Value("${xxl.job.executor.appname}")
	private String jobName;


	/**
	 * 分页查询定时任务
	 * @return
	 */
	@Override
	public IPage<XxlJobInfoEntity> xxlJobInfoPage(XxlJobDTO xxlJobDTO) {
		String jobDesc = xxlJobDTO.getJobDesc();
		String author = xxlJobDTO.getAuthor();
		Integer pageIndex = xxlJobDTO.getPageIndex();
		Integer pageSize = xxlJobDTO.getPageSize();
		Integer jobGroup = xxlJobDTO.getJobGroup();
		LambdaQueryWrapper<XxlJobInfoEntity> xxlJobInfoWrapper = Wrappers.lambdaQuery();
		xxlJobInfoWrapper
			.eq(ObjectUtil.isNotEmpty(jobGroup),XxlJobInfoEntity::getJobGroup, jobGroup)
			.like(StringUtils.isNotBlank(jobDesc),XxlJobInfoEntity::getJobDesc,jobDesc)
			.like(StringUtils.isNotBlank(author),XxlJobInfoEntity::getAuthor,author)
			.orderByDesc(XxlJobInfoEntity::getUpdateTime);
		return page(new Page<>(pageIndex<1?1:pageIndex, pageSize<1?10:pageSize),xxlJobInfoWrapper);
	}

	/**
	 * 保存或修改任务
	 * @param xxlJobInfoEntity
	 * @return
	 */
	@Override
	public Boolean saveOrUpdateXxlJob(XxlJobInfoEntity xxlJobInfoEntity) {
		xxlJobInfoEntity.setGlueRemark("GLUE代码初始化");
		Date date = new Date();
		if (xxlJobInfoEntity.getId()==null){
			xxlJobInfoEntity.setAddTime(date);
			xxlJobInfoEntity.setUpdateTime(date);
			xxlJobInfoEntity.setGlueUpdatetime(date);
			if (GlueTypeEnum.GLUE_SHELL.toString().equals(xxlJobInfoEntity.getGlueType())){
				xxlJobInfoEntity.setGlueSource(xxlJobConfig.getGlueSourceShell());
			}else if (GlueTypeEnum.GLUE_POWERSHELL.toString().equals(xxlJobInfoEntity.getGlueType())){
				xxlJobInfoEntity.setGlueSource(xxlJobConfig.getGlueSourcePowerShell());
			}else {
				xxlJobInfoEntity.setGlueSource("");
			}
		}else {
			xxlJobInfoEntity.setUpdateTime(date);
		}
		return saveOrUpdate(xxlJobInfoEntity);
	}

	/**
	 * 获取调度类型列表
	 * @return
	 */
	@Override
	public List<XxlJobDictVo> scheduleTypeList() {
		List<XxlJobDictVo> results = new ArrayList<>();
		results.add(new XxlJobDictVo("无","NONE"));
		results.add(new XxlJobDictVo("CRON", "CRON"));
		results.add(new XxlJobDictVo("固定频率", "FIX_RATE"));
		return results;
	}

	/**
	 * 返回运行模式列表
	 * @return
	 */
	@Override
	public List<XxlJobDictVo> glueType() {
		List<XxlJobDictVo> results = new ArrayList<>();
		results.add(new XxlJobDictVo("BEAN", GlueTypeEnum.BEAN.toString()));
		results.add(new XxlJobDictVo("GLUE(Shell)",GlueTypeEnum.GLUE_SHELL.toString()));
		results.add(new XxlJobDictVo("GLUE(PowerShell)",GlueTypeEnum.GLUE_POWERSHELL.toString()));
		return results;
	}

	/**
	 * 获取路由策略列表
	 * @return
	 */
	@Override
	public List<XxlJobDictVo> executorRouteStrategy() {
		List<XxlJobDictVo> results = new ArrayList<>();
		results.add(new XxlJobDictVo("第一个", "FIRST"));
		results.add(new XxlJobDictVo("最后一个", "LAST"));
		results.add(new XxlJobDictVo("轮询", "ROUND"));
		results.add(new XxlJobDictVo("随机", "RANDOM"));
		results.add(new XxlJobDictVo("一致性HASH", "CONSISTENT_HASH"));
		results.add(new XxlJobDictVo("最不经常使用", "LEAST_FREQUENTLY_USED"));
		results.add(new XxlJobDictVo("最近最久未使用", "LEAST_RECENTLY_USED"));
		results.add(new XxlJobDictVo("故障转移", "FAILOVER"));
		results.add(new XxlJobDictVo("忙碌转移", "BUSYOVER"));
		results.add(new XxlJobDictVo("分片广播", "SHARDING_BROADCAST"));
		return results;
	}

	/**
	 * 调度过期策略列表
	 * @return
	 */
	@Override
	public List<XxlJobDictVo> misfireStrategyList() {
		List<XxlJobDictVo> results = new ArrayList<>();
		results.add(new XxlJobDictVo("立即执行一次", "FIRE_ONCE_NOW"));
		results.add(new XxlJobDictVo("忽略", "DO_NOTHING"));
		return results;
	}

	/**
	 * 阻塞处理策略列表
	 * @return
	 */
	@Override
	public List<XxlJobDictVo> executorBlockStrategy() {
		List<XxlJobDictVo> results = new ArrayList<>();
		results.add(new XxlJobDictVo("单机串行", "SERIAL_EXECUTION"));
		results.add(new XxlJobDictVo("丢弃后续调度", "DISCARD_LATER"));
		results.add(new XxlJobDictVo("覆盖之前调度", "COVER_EARLY"));
		return results;
	}

	/**
	 * 启动定时任务
	 * @param id
	 * @return
	 */
	@Override
	public Boolean start(Integer id) {
		String url = xxlJobConfig.getAdminAddresses() + xxlJobConfig.getStartUrl() + "?id=" + id;
		RestTemplate restTemplate = new RestTemplate();
		HttpHeaders headers = new HttpHeaders();
		headers.set("cookie", "XXL_JOB_LOGIN_IDENTITY="+xxlJobUtil.login());
		HttpEntity<String> entity = new HttpEntity<>(null, headers);
		ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
		if (response.getStatusCode().is2xxSuccessful()) {
			return true;
		}
		throw new ServiceException(response.getBody());
	}

	/**
	 * 停止定时任务
	 * @param id
	 * @return
	 */
	@Override
	public Boolean stop(Integer id) {
		String url = xxlJobConfig.getAdminAddresses() + xxlJobConfig.getStopUrl() + "?id=" + id;
		RestTemplate restTemplate = new RestTemplate();
		HttpHeaders headers = new HttpHeaders();
		headers.set("cookie", "XXL_JOB_LOGIN_IDENTITY="+xxlJobUtil.login());
		HttpEntity<String> entity = new HttpEntity<>(null, headers);
		ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
		if (response.getStatusCode().is2xxSuccessful()) {
			return true;
		}
		throw new ServiceException(response.getBody());
	}

	/**
	 * 触发定时任务
	 * @param xxlJobDTO
	 * @return
	 */
	@Override
	public Boolean trigger(XxlJobDTO xxlJobDTO) {
		String url = xxlJobConfig.getAdminAddresses() + xxlJobConfig.getTriggerUrl();
		RestTemplate restTemplate = new RestTemplate();
		HttpHeaders headers = new HttpHeaders();
		headers.set("cookie", "XXL_JOB_LOGIN_IDENTITY="+xxlJobUtil.login());
		headers.set(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE);
		MultiValueMap<String,Object> requestBody = new LinkedMultiValueMap<>();
		requestBody.add("id",xxlJobDTO.getId());
		requestBody.add("executorParam",xxlJobDTO.getExecutorParam());
		requestBody.add("addressList",xxlJobDTO.getAddressList());
		HttpEntity<MultiValueMap<String,Object>> entity = new HttpEntity<>(requestBody, headers);
		ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
		if (response.getStatusCode().is2xxSuccessful()) {
			return true;
		}
		throw new ServiceException(response.getBody());
	}

	/**
	 * 保存脚本代码
	 * @param xxlJobDTO
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean glueIdeSave(XxlJobDTO xxlJobDTO) {
		Integer id = xxlJobDTO.getId();
		String glueSource = xxlJobDTO.getGlueSource();
		String glueRemark = xxlJobDTO.getGlueRemark();
		if (id != null) {
			Date date = new Date();
			//保存任务中的脚本代码
			XxlJobInfoEntity xxlJobInfoEntity = getById(id);
			XxlJobInfoEntity updateEntity = new XxlJobInfoEntity();
			updateEntity.setId(id);
			updateEntity.setGlueSource(glueSource);
			updateEntity.setGlueRemark(glueRemark);
			updateEntity.setGlueUpdatetime(date);
			this.updateById(updateEntity);
			//保存脚本至历史版本中
			XxlJobLogglueEntity xxlJobLogglueEntity = new XxlJobLogglueEntity();
			xxlJobLogglueEntity.setJobId(id);
			xxlJobLogglueEntity.setGlueType(xxlJobInfoEntity.getGlueType());
			xxlJobLogglueEntity.setGlueSource(glueSource);
			xxlJobLogglueEntity.setGlueRemark(glueRemark);
			xxlJobLogglueEntity.setAddTime(date);
			xxlJobLogglueEntity.setUpdateTime(date);
			xxlJobLogglueService.save(xxlJobLogglueEntity);
			return true;
		}
		return null;
	}

	/**
	 * 获取脚本代码列表
	 * @param jobId
	 * @return
	 */
	@Override
	public List<XxlJobLogglueEntity> getGlueSourceList(Integer jobId) {
		LambdaQueryWrapper<XxlJobLogglueEntity> xxlJobGlueSourceWrapper = Wrappers.lambdaQuery();
		xxlJobGlueSourceWrapper
			.select(XxlJobLogglueEntity::getId,XxlJobLogglueEntity::getGlueRemark)
			.eq(XxlJobLogglueEntity::getJobId,jobId);
		return xxlJobLogglueService.list(xxlJobGlueSourceWrapper);
	}

	/**
	 * 删除定时任务
	 * @param id
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public Boolean removeJob(Integer id) {
		removeById(id);
		LambdaQueryWrapper<XxlJobLogglueEntity> xxlJobLogglueWrapper = Wrappers.lambdaQuery();
		xxlJobLogglueWrapper.eq(XxlJobLogglueEntity::getJobId,id);
		xxlJobLogglueService.remove(xxlJobLogglueWrapper);
		return true;
	}

	@Override
	public void addJob(XxlJobInfoEntity job){
		XxlJobGroupEntity groupEntity = groupService.getOne(Wrappers.<XxlJobGroupEntity>lambdaQuery().eq(XxlJobGroupEntity::getAppName, jobName).last("limit 1"));
		job.setJobGroup(groupEntity.getId());
		job.setAddTime(new Date());
		job.setUpdateTime(new Date());
		job.setAuthor("lowcode_bi");
		job.setAlarmEmail("waytale@waytale.cn");
		job.setScheduleType("CRON");
		job.setMisfireStrategy("DO_NOTHING");
		job.setExecutorRouteStrategy("FIRST");
		job.setExecutorHandler("SqlHandler");
		job.setExecutorFailRetryCount(0);
		job.setGlueType("BEAN");
		job.setExecutorBlockStrategy("SERIAL_EXECUTION");
		job.setExecutorTimeout(0);
		job.setGlueUpdatetime(new Date());
		saveOrUpdateXxlJob(job);
	}

	@Override
	public XxlJobInfoEntity get(Integer id){
		return getById(id);
	}

	@Override
	public String getCronById(Integer id){
		XxlJobInfoEntity job = get(id);
		if(null == job){
			return null;
		}
		return job.getScheduleConf();
	}
}




